/*
 * Mobile Web Components v1.0
 * http://projects.developer.nokia.com/mobile_web_components
 *
 * Copyright 2011, Nokia
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 */

/*
 * DOM addEvent and removeEvent convenience functions
 * from http://ejohn.org/projects/flexible-javascript-events/
 *
 */
 
function addEvent( obj, type, fn ) {
  if ( obj.attachEvent ) {
    obj['e'+type+fn] = fn;
    obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
    obj.attachEvent( 'on'+type, obj[type+fn] );
  } else
    obj.addEventListener( type, fn, false );
}
function removeEvent( obj, type, fn ) {
  if ( obj.detachEvent ) {
    obj.detachEvent( 'on'+type, obj[type+fn] );
    obj[type+fn] = null;
  } else
    obj.removeEventListener( type, fn, false );
}

/*
 * DOM hasClass, addClass and removeClass convenience functions
 * from http://snipplr.com/view/3561/addclass-removeclass-hasclass/
 *
 */
 
function hasClass(ele,cls) {
	return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
	if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
	if (hasClass(ele,cls)) {
		var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
		ele.className=ele.className.replace(reg,' ');
	}
}

/*
 * SlidingDrawer({id:string, open:boolean})
 * usage: var main_menu = new SlidingDrawer({"id":"top", "open":true});
 *
 */

function SlidingDrawer(options) {
	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		indicator: null,
		open: null,
		nav: null,
		init: function() {
			// create required additional component DOM elements
			self.indicator = document.createElement("div");
			self.component.appendChild(self.indicator);
			self.nav = component.getElementsByTagName('nav')[0];
			self.height = self.nav.offsetHeight;
			
			if (self.options.open == true) {
				self.open = true;
			} else {
				self.open = false;
			}

			// stopPropogration on all contained links in order to avoid
			// having the component toggle when a menu item is pressed
			var a = component.getElementsByTagName("a");
			for ( var i = 0; i < a.length; i++ ) {
				a[i].onclick = function(e) {
					e.stopPropagation();
				}
			}
			self.component.onclick = function() {
				self.toggle();
				this.blur();
				return false;
			};
			self.update();
		},
		toggle: function(state) {
			if (state == "open")  {
				self.open = false;
			}
			if (state == "close") {
				self.open = true;
			}
			self.open = self.open ? false : true ;
			self.update();
		},
		update: function() {
			if (self.open) {
				self.nav.style.pixelHeight = self.height;
				addClass(self.component, "open");
			} else {
				self.nav.style.pixelHeight = 0;
				removeClass(self.component, "open");
			}
		}
	};
	self.init();
	return self;
}

/*
 * Button({id:string, callback:string})
 * usage: var options = {"id":"tweet", "callback":"tweetThis"};
 *        var btn = new Button(options);
 *
 */
 
function Button(options) {
	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		callback: null,
		init: function() {
			// set up the callback and onclick event
			self.callback = self.options.callback?self.options.callback:"self.log";
			self.component.onclick = function (e) {
				var ref = this;
				self.press(self.component);
				addClass(ref, "select");
				setTimeout ( function() {removeClass(ref, "select");}, 250);
				return false;
			}
		},
		press: function(element) {
			eval(self.callback)(self.options.id);
		},
		log: function() {
			console.log("No callback defined for: " + self.options.id);
			alert("No callback defined for: " + self.options.id);
		}
	};
	self.init();
	return self;
}
 
/*
 * PopupMenu({id:string, trigger:string, callback:string})
 * usage: var options = {"id":"top", "trigger":"share", "callback":"shareViaSocialNetwork"};
 *        var menu = new PopupMenu(options);
 *
 */

function PopupMenu(options) {
	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		document: null,
		trigger: null,
		visible: false,
		callback: null,
		init: function() {
			self.trigger = document.getElementById(self.options.trigger);
			self.document = document;
			
			// set up the callback and trigger element
			self.callback = self.options.callback?self.options.callback:"self.log";
			self.trigger.onclick = function (e) {
				self.show();
				return false;
			}
			
			// set up all a elements within menu to call this.select() onclick
			var a = self.component.getElementsByTagName("a");
			var total = a.length;
			for ( var i = 0; i < total; i++ ) {
				a[i].onclick = function(e) {
					self.select(this);
					return false;
				}
			}
			addClass(self.component, "hidden");
		},
		show: function() {
			if (!hasClass(self.component,"hidden")) {
				self.hide();
				return;
			}
			self.component.style.display = "block";
			self.component.style.top = (self.trigger.offsetTop - self.component.offsetHeight) + "px";
			self.component.style.left = ((self.trigger.offsetLeft - self.component.offsetWidth/2) + (self.trigger.offsetWidth/2)) + "px";
			var _offset = 20;
			var _width = (window.innerWidth>0)?window.innerWidth:screen.width;
			if (self.component.offsetLeft + self.component.offsetWidth >= _width) {
				var position = _width - (self.component.offsetWidth + _offset);
				self.component.style.left = position + "px";
			}
			if (self.component.offsetLeft < 0) {
				var position = 0 + _offset;
				self.component.style.left = 0 + "px";
			}
			removeClass(self.component, "hidden");
			// set up unction to hide menu if user press anywhere but the menu items
			setTimeout ( function() {
				self.document.onclick = function() {
					this.onclick = null;
					self.hide();
				};
			}, 50 );
		},
		hide: function() {
			addClass(self.component, "hidden");
			self.document.onclick = function() {};
			// remove the component after it has faded from view
			setTimeout ( function() {
				self.component.style.display = "none";
			}, 500);
		},
		select: function(item) {
			eval(self.callback)(item);
			self.hide();
		},
		log: function(message) {
			console.log(message);
			alert(message);
		}
	};
	self.init();
	return self;
}

/*
 * Collapsible({id:string, toggle:string})
 * usage: var details = new Collapsible({"id":"details","toggle":"open"});
 *
 */

function Collapsible(options) {
	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		open: false,
		init: function() {
			// set up the component; control = <dt>, content = <dd>
			self.control = self.component.getElementsByTagName('dt')[0];
			self.content = self.component.getElementsByTagName('dd')[0];
			self.height = self.content.offsetHeight;
			
			if (options.open == true) {
				self.open = true;
			}
			self.control.onclick = function() {
				self.toggle();
				var ref = this;
				addClass(ref, "select");
				setTimeout ( function() {removeClass(ref, "select");}, 250);
				return false;
			}
			self.update();
		},
		toggle: function(state) {
			if (state) {
				self.open = state == "open" ? true : false;
			} else {
				self.open = self.open ? false : true ;
			}
			self.update();
		},
		update: function() {
			if (self.open) {
				self.content.style.pixelHeight = self.height;
				addClass(self.component, "open");
			} else {
				self.content.style.pixelHeight = 0;
				removeClass(self.component, "open");
			}
		}
	};
	self.init();
	return self;
}

/*
 * Scrollable({id:string, border:boolean})
 * usage: var map = new Scrollable({"id":"map","border":true});
 *
 */
 
function Scrollable(options) {
	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		height: 300,
		busy: false,
		HORIZONTAL: -1, // static definition
		VERTICAL: 1, // static definition
		init: function() {
			// create additional viewport and content <div> container elements
			self.viewport = document.createElement("div");
			self.viewport.className = "viewport";
			self.content = document.createElement("div");
			self.content.className = self.options.border ? "content border" : "content" ;
			self.content.innerHTML = self.component.innerHTML;
			self.component.innerHTML = null;
			self.viewport.appendChild(self.content);
			
			// create the <nav> element and attach navigational tools via <ul>...
			self.tools = document.createElement("nav");
			self.tools.className = "tools";
			var tools = document.createElement("ul");
			var items = new Array("up", "down", "left", "right");
			for (item in items) {
				self[items[item]] = document.createElement("li");
				self[items[item]].className = items[item];
	
				var a = document.createElement("a");
				a.direction = items[item];
				a.onclick = function() {
					var ref = self[this.direction];
					self.scroll(this.direction);
					addClass(ref, "select");
					setTimeout ( function() {removeClass(ref, "select");}, 250);
					return false;
				};
				var img = document.createElement("img");
				img.setAttribute("src", "resources/images/arrows.png");
				
				a.appendChild(img);
				self[items[item]].appendChild(a);
				tools.appendChild(self[items[item]]);
			};
			self.tools.appendChild(tools);
			self.component.appendChild(self.viewport);
			self.component.appendChild(self.tools);
			
			// set the component and viewport pixel heights
			self.component.style.pixelHeight = self.options.height ? self.options.height : self.height;
			self.viewport.style.pixelHeight = self.options.height ? self.options.height : self.height;
			
			self.adjust();
			self.update();
		},
		scroll: function(direction) {
			var origin,target,distance,axis,fudge = 2; // annoying as 0 just doesn't work...
			// determine scroll target location on the requested axis
			if (direction == "up" || direction == "down") {
				axis = self.VERTICAL;
				origin = self.viewport.scrollTop;
				distance = self.viewport.offsetHeight;
				
				if (direction == "up") {
					target = origin - distance;
					var minimum = 0;
					target <= minimum ? target = minimum-fudge : false; 
				} else {
					target = origin + distance;
					var maximum = self.content.offsetHeight - self.viewport.offsetHeight;
					target >= maximum ? target = maximum : false;
				}
			}
			if (direction == "left" || direction == "right") {
				axis = self.HORIZONTAL;
				origin = self.viewport.scrollLeft;
				distance = self.viewport.offsetWidth;
				
				if (direction == "left") {
					target = origin - distance;
					var minimum = 0;
					target <= minimum ? target = minimum-fudge : false;
				} else {
					target = origin + distance;
					var maximum = self.content.offsetWidth - self.viewport.offsetWidth;
					target >= maximum ? target = maximum : false;
				}
			}
			// actually perform animated scroll to target along axis...
			self.update(target, axis);
		},
		update: function(target, axis) {
			if (self.busy || !target || !axis) {
				return false;
			}
			self.busy = true;
			// animate the scroll to the target location along the requested axis
			self.interval = setInterval(function() {
				var current, delta;
				axis == self.VERTICAL? current = self.viewport.scrollTop : current = self.viewport.scrollLeft;
				delta = Math.ceil((target-current) * 0.33);
				axis == self.VERTICAL? self.viewport.scrollTop += delta : self.viewport.scrollLeft += delta;
				if (delta == 0) {
					clearInterval(self.interval);
					self.busy = false;
					self.adjust();
				}
			}, 40);
		},
		adjust: function() {
			var fudge = 2; // annoying as 0 just doesn't work... 
			// hide and show respective navigation tools as required
			if (self.viewport.scrollTop <= fudge) {
				addClass(self.up, "hidden");
			} else {
				removeClass(self.up, "hidden");
			}
			if (self.viewport.scrollTop >= (self.content.offsetHeight - self.viewport.offsetHeight)) {
				addClass(self.down, "hidden");
			} else {
				removeClass(self.down, "hidden");
			}
			if (self.viewport.scrollLeft <= fudge) {
				addClass(self.left, "hidden");
			} else {
				removeClass(self.left, "hidden");
			}
			if (self.viewport.scrollLeft >= (self.content.offsetWidth - self.viewport.offsetWidth)) {
				addClass(self.right, "hidden");
			} else {
				removeClass(self.right, "hidden");
			}
		}		
	};
	self.init();
	return self;
}

			var slideshow_options = {
				"id":"example-slideshow",
				"loop":"false",
				"autoplay":"1"
			};

/*
 * Slideshow({id:string, loop:boolean, autoplay:number})
 * usage: var options = {"id":"vacation", "index":1, "loop":true,"autoplay":5000};
 *        var photos = new Slideshow(options);
 *
 * NOTE: autoplay:number should be supplied in milliseconds (ie: 1000 = 1 second)
 *
 */
 
 function Slideshow(options) {
 	var component = document.getElementById(options.id);
	var self = {
		options: options,
		component: component,
		index:0,current:0,
		init: function() {
			self.slides = self.component.getElementsByTagName('ol')[0].getElementsByTagName('li');
			
			// create back button
			var back = document.createElement("li");
			self.back_btn = document.createElement("a");
			self.back_btn.className = 	self.back_btn.innerHTML = "back";
			self.back_btn.onclick = function() {
				self.back();
				addClass(self.back_btn, "select");
				setTimeout ( function() {removeClass(self.back_btn, "select");}, 250);
				return false;
			}
			back.appendChild(self.back_btn);
			
			// create slide count element
			self.count_txt = document.createElement("li");
			
			// create next button
			var next = document.createElement("li");
			self.next_btn = document.createElement("a");
			self.next_btn.className = self.next_btn.innerHTML = "next";
			self.next_btn.onclick = function() {
				addClass(self.next_btn, "select");
				setTimeout ( function() {removeClass(self.next_btn, "select");}, 250);
				self.next();
				return false;
			}
			next.appendChild(self.next_btn);
			
			var ul = document.createElement("ul");
			ul.appendChild(back);
			ul.appendChild(self.count_txt);
			ul.appendChild(next);
			
			// add button elements to <nav>, and add <nav> to component 
			var nav = document.createElement("nav");
			nav.appendChild(ul);
			self.component.appendChild(nav);
			
			// if an index (non-zero-based) is specified in options, use it...
			self.index = options.index ? options.index-1 : 0 ;
			for (slide in self.slides) {
				self.slides[slide].className = "hidden";
			}
			self.current = self.index;
			self.slides[self.index].className = "";
			// set-up autoplay (in milliseconds) is specified in options
			if (self.options.autoplay) {
				if (self.options.delay < 1000 || !self.options.delay) {
					self.options.delay = 4000;
				}
			}
			self.update();
		},
		back: function() {
			self.index--;
			self.update();
		},
		next: function() {
			self.index++;
			self.update();
		},
		goto: function(slide) {
			self.index = slide - 1;
			self.update();
		},
		update: function() {
			// clear self.interval if it happens to exist
			(self.interval)?clearInterval(self.interval):false;
			// handle options.loop
			var total = self.slides.length;
			if (self.index >= total) {
				self.options.loop ? self.index = 0 : self.index = (total-1);
			}
			if (self.index < 0) {
				self.options.loop ? self.index = (total-1) : self.index = 0;
			}
			// update display
			self.slides[self.current].className = "hidden"
			self.slides[self.index].className = "";
			self.count_txt.innerHTML = (self.index + 1) + "/" + total;
			self.current = self.index;
			// reset self.interval if autoplay was specified
			if (self.options.autoplay) {
				self.interval =	setTimeout ( function() {self.next()}, self.options.delay );
			}
		}
	};
	self.init();
	return self;
}

/*
 * Sparkline({id:string})
 * usage:
 * var data = new Sparkline({"id":"temperature"});
 * .. turns the following HTML element into a sparkline
 *
 * <span id="temperature">1,6,4,3,25,8,12,1,-1,55,22</span>
 *
 * alternate usage:
 * var a = document.getElementsByTagName("*") || document.all;
 * for ( var i = 0; i < a.length; i++ ) {
 *   if (hasClass(a[i], "sparkline")) {
 *     Sparkline(a[i]);
 *   }
 * } 
 * ... turns the following HTML elements into sparklines
 *
 * <span class="sparkline">5,10,8,2,20,2,1,6,40,2,1,7,9,19</span>
 * <span class="sparkline">32,54,23,65</span>
 * <span class="sparkline">1,8,66,62,13,18,9,12</span>  
 *
 * Based on:
 * Javascript Sparklines Library
 * Written By John Resig
 * http://ejohn.org/projects/jspark/
 * 
 * This work is tri-licensed under the MPL, GPL, and LGPL:
 * http://www.mozilla.org/MPL/
 */

function Sparkline(options) {
	var component = options.id?document.getElementById(options.id):options;
	var self = {
		options: options,
		component: component,
		data: [],
		canvas: null,
		init: function() {
			!self.options.id?self.options=null:false;
			// copy data from HTML to the component itself
			self.data = self.component.innerHTML.split(',');
			while (self.component.childNodes.length > 0) {
				self.component.removeChild(self.component.firstChild);
			}
			
			// set-up the component size, style, <canvas>, etc.
			var nw = "auto";
			var nh = "auto";
			if ( window.getComputedStyle ) {
				nw = window.getComputedStyle( self.component, null ).width;
				nh = window.getComputedStyle( self.component, null ).height;
			}
			
			if ( nw != "auto" ) nw = nw.substr( 0, nw.length - 2 );
			if ( nh != "auto" ) nh = nh.substr( 0, nh.length - 2 );
			
			var f = 2;
			var w = ( nw == "auto" || nw == 0 ? self.data.length * f : nw - 0 );
			var h = ( nh == "auto" || nh == 0 ? "1em" : nh );
			
			self.canvas = document.createElement("canvas");
			
			if ( self.canvas.getContext ) self.component.style.display = 'inline';
			else return false;
			
			self.canvas.style.height = h;
			self.canvas.style.width = w;
			self.canvas.width = w;
			self.component.appendChild( self.canvas );
			
			var h = self.canvas.offsetHeight;
			self.canvas.height = h;
			
			var min = 9999;
			var max = -1;
			
			for ( var i = 0; i < self.data.length; i++ ) {
				self.data[i] = self.data[i] - 0;
				if ( self.data[i] < min ) min = self.data[i];
				if ( self.data[i] > max ) max = self.data[i];
			}
			
			// draw the data to the canvas
			if ( self.canvas.getContext ) {
				var draw = self.canvas.getContext("2d");
				draw.strokeStyle = "red";
				draw.lineWidth = 1.0;
				draw.beginPath();
				
				for ( var i = 0; i < self.data.length; i++ ) {
				  if ( i == 0 )
				  draw.moveTo( (w / self.data.length) * i, h - (((self.data[i] - min) / (max - min)) * h) );
				  
				  draw.lineTo( (w / self.data.length) * i, h - (((self.data[i] - min) / (max - min)) * h) ); 
				}
				
				draw.stroke();
				self.component.style.display = 'inline';
			}			
		},
	};
	self.init();
	return self;
}